home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-04 / gs24src.zip / IALLOC.C < prev    next >
C/C++ Source or Header  |  1992-03-04  |  12KB  |  366 lines

  1. /* Copyright (C) 1989, 1990, 1991 Aladdin Enterprises.  All rights reserved.
  2.    Distributed by Free Software Foundation, Inc.
  3.  
  4. This file is part of Ghostscript.
  5.  
  6. Ghostscript is distributed in the hope that it will be useful, but
  7. WITHOUT ANY WARRANTY.  No author or distributor accepts responsibility
  8. to anyone for the consequences of using it or for whether it serves any
  9. particular purpose or works at all, unless he says so in writing.  Refer
  10. to the Ghostscript General Public License for full details.
  11.  
  12. Everyone is granted permission to copy, modify and redistribute
  13. Ghostscript, but only under the conditions described in the Ghostscript
  14. General Public License.  A copy of this license is supposed to have been
  15. given to you along with Ghostscript so you can know your rights and
  16. responsibilities.  It should be in a file named COPYING.  Among other
  17. things, the copyright notice and this notice must be preserved on all
  18. copies.  */
  19.  
  20. /* ialloc.c */
  21. /* Memory allocator for Ghostscript interpreter */
  22. #include <stdio.h>        /* for NULL */
  23. #include "gs.h"
  24. #include "memory_.h"
  25. #include "alloc.h"
  26. #include "astate.h"
  27.  
  28. #ifdef DEBUG
  29. extern char gs_debug[128];
  30. #endif
  31.  
  32. /* Forward references */
  33. private int alloc_add_chunk(P2(alloc_state_ptr, uint));
  34. private char *alloc_large(P3(alloc_state_ptr, uint, const char *));
  35. private void alloc_free_large(P3(char *, uint, const char *));
  36.  
  37. /* The only allocator instance (for now). */
  38. private alloc_state as_current;
  39. alloc_state_ptr alloc_state_current = &as_current;
  40.  
  41. /* Debugging printout */
  42. #ifdef DEBUG
  43. #  define alloc_print(rtag, tag, blk, sz)\
  44.     if ( gs_debug['A'] )\
  45.       fprintf(gs_debug_out, "[a:%c:%c:%s] %lx(%u)\n", rtag, tag,\
  46.           client_name, (ulong)blk, sz)
  47. #  define alloc_print_large(rtag, tag, blk, sz)\
  48.     if ( gs_debug['A'] | gs_debug['a'] )\
  49.       fprintf(gs_debug_out, "[a:%c:%c:%s] %lx(%u)\n", rtag, tag,\
  50.           client_name, (ulong)blk, sz)
  51. #else
  52. #  define alloc_print(rtag, tag, blk, sz)
  53. #  define alloc_print_large(rtag, tag, blk, sz)
  54. #endif
  55.  
  56. /* ------ Initialize/status ------ */
  57.  
  58. /* Initialize the allocator */
  59. void
  60. alloc_init(proc_alloc_t palloc, proc_free_t pfree, uint chunk_size)
  61. {    register alloc_state_ptr ap = &as_current;
  62.     memset(ap, 0, sizeof(alloc_state));    /* do it all at once */
  63.     ap->chunk_size = chunk_size;
  64.     ap->big_size = chunk_size / 4;
  65.     ap->palloc = palloc;
  66.     ap->pfree = pfree;
  67.        {    extern void alloc_save_init(P0());
  68.         alloc_save_init();
  69.        }
  70. }
  71.  
  72. /* Return the status of the allocator: space used, total space. */
  73. void
  74. alloc_status(long *pused, long *ptotal)
  75. {    register alloc_state_ptr ap = &as_current;
  76.     *pused = (ap->cbot - ap->cbase) + (ap->climit - ap->ctop) + ap->used;
  77.     *ptotal = ap->total;
  78. }
  79.  
  80. /* ------ Allocation and freeing ------ */
  81.  
  82. /* Allocate an object.  Return 0 if not enough room. */
  83. char *
  84. alloc(uint num_elts, uint elt_size, const char *client_name)
  85. {    register alloc_state_ptr ap = &as_current;
  86.     uint size = num_elts * elt_size;
  87.     uint block_size;
  88.     uint left;
  89.     if ( size >= ap->big_size )
  90.        {    /* Large object, do a separate malloc. */
  91.         char *block = alloc_large(ap, size, client_name);
  92.         if ( block != NULL ) return block;
  93.         if ( size > ap->chunk_size )
  94.             return 0;    /* can't alloc */
  95.        }
  96.     block_size = align_round(size);
  97.     if ( block_size <= max_chain_size )
  98.        {    /* See if we can use a freed block. */
  99.         char **fptr = &ap->free[block_size >> log2_align_mod];
  100.         char *block = *fptr;
  101.         if ( block != 0 )
  102.            {    *fptr = *(char **)block;
  103.             alloc_print('+', '#', block, size);
  104.             return block;
  105.            }
  106.        }
  107.     left = ap->ctop - ap->cbot;
  108.     if ( block_size > left )
  109.        {    uint csize = ap->chunk_size;
  110.         while ( !alloc_add_chunk(ap, csize) )
  111.            {    alloc_print('+', '?', (ulong)0, size);
  112.             /* Things are desperate, but perhaps not hopeless. */
  113.             if ( (csize >>= 1) < block_size )
  114.                 return 0;    /* no hope */
  115.            }
  116.        }
  117.     if ( elt_size == 1 )
  118.        {    /* Unaligned block */
  119.         ap->ctop -= size;
  120.         alloc_print('+', '>', ap->ctop, size);
  121.         return (char *)ap->ctop;
  122.        }
  123.     else
  124.        {    /* Aligned block */
  125.         char *block = (char *)ap->cbot;
  126.         ap->cbot += block_size;
  127.         alloc_print('+', '<', block, size);
  128.         return block;
  129.        }
  130. }
  131.  
  132. /* Free an object, if possible. */
  133. /* Note that if a save is in effect, objects in chunks older than */
  134. /* the save, and objects allocated with malloc before the save, */
  135. /* must not be freed. */
  136. void
  137. alloc_free(char *cobj, uint num_elts, uint elt_size, const char *client_name)
  138. {    register alloc_state_ptr ap = &as_current;
  139.     uint size = num_elts * elt_size;
  140.     uint block_size;
  141.     if ( size >= ap->big_size )
  142.        {    /* Object was allocated with malloc. */
  143.         alloc_free_large(cobj, size, client_name);
  144.         return;
  145.        }
  146. #define obj ((byte *)cobj)
  147.     else if ( obj == ap->ctop )
  148.        {    /* Don't free the object if we're in a save and */
  149.         /* this object wasn't allocated since the save. */
  150.         if ( ap->save_level == 0 ||
  151.              ap->current.save_level >= ap->save_level ||
  152.              /* We know the current chunk is the same as */
  153.              /* the one in as->saved->state */
  154.              obj < ap->saved_ctop
  155.            )
  156.             ap->ctop += size;
  157.         alloc_print('-', '>', obj, size);
  158.         return;
  159.        }
  160.     else if ( obj + (block_size = align_round(size)) == ap->cbot )
  161.        {    /* Freeing an aligned object.  Same check. */
  162.         if ( ap->save_level == 0 ||
  163.              ap->current.save_level >= ap->save_level ||
  164.              obj >= ap->saved_cbot
  165.            )
  166.             ap->cbot = obj;
  167.         alloc_print('-', '<', obj, size);
  168.         return;
  169.        }
  170.     else if ( !ptr_is_in_chunk(obj, &ap->current) )
  171.        {    /* In another segment, check its save level. */
  172.         int level = ap->save_level;
  173.         alloc_chunk *cp = ap->current.next;
  174.         for ( ; ; cp = cp->next )
  175.           { if ( cp != 0 )
  176.               { switch ( cp->save_level - level )
  177.               {
  178.               case 0:
  179.                 if ( ptr_is_in_chunk(obj, cp) )
  180.                   { if ( ptr_lt(obj, cp->bot) ) goto pbf;
  181.                   else break;
  182.                   }
  183.                 else continue;
  184.               case -1:
  185.                 /* Might be alloc'ed since the save, */
  186.                 /* or might not be aligned. */
  187.                 if ( ptr_lt(obj, ap->saved_cbot) )
  188.                   goto pbf;
  189.               }
  190.               }
  191.             /* Older save level, not freeable. */
  192.             alloc_print('-', '\\', obj, size);
  193.             return;
  194.           }
  195. pbf:        /* If we get here, OK to put the block on a free list. */
  196.         ;
  197.        }
  198.     else if ( obj >= ap->cbot )    /* not aligned object, punt */
  199.        {    alloc_print('-', '~', obj, size);
  200.         return;
  201.        }
  202.     /* Put on a free list if small enough */
  203.     alloc_print('-', '#', obj, size);
  204.     if ( block_size <= max_chain_size && block_size >= sizeof(char **) )
  205.        {    char **fptr = &ap->free[block_size >> log2_align_mod];
  206.         *(char **)cobj = *fptr;
  207.         *fptr = cobj;
  208.        }
  209. #undef obj
  210. }
  211.  
  212. /* Grow an object.  This may require allocating a new copy. */
  213. /* Return 0 if not enough room. */
  214. /****** Note: the object must have been allocated at
  215.  ****** the current save level. */
  216. byte *
  217. alloc_grow(byte *obj, uint old_num, uint new_num, uint elt_size,
  218.   const char *client_name)
  219. {    register alloc_state_ptr ap = &as_current;
  220.     uint old_size = old_num * elt_size;
  221.     uint new_size = new_num * elt_size;
  222.     byte *nobj;
  223.     if ( new_size == old_size ) return obj;
  224.     if ( new_size < ap->big_size ) /* try to grow in place */
  225.       { uint old_block_size;
  226.         uint new_block_size;
  227.         if ( obj == ap->ctop )
  228.           { /* Might be able to grow in place */
  229.         uint diff = new_size - old_size;
  230.         if ( diff <= ap->ctop - ap->cbot )
  231.           { alloc_print('>', '>', obj, new_size);
  232.             ap->ctop -= diff;
  233.             memcpy(ap->ctop, obj, old_size);
  234.             return ap->ctop;
  235.           }
  236.           }
  237.         old_block_size = align_round(old_size);
  238.         new_block_size = align_round(new_size);
  239.         if ( obj + old_block_size == ap->cbot )
  240.           { /* Might be able to grow in place */
  241.         uint diff = new_block_size - old_block_size;
  242.         if ( diff <= ap->ctop - ap->cbot )
  243.           { alloc_print('>', '<', obj, new_size);
  244.             ap->cbot += diff;
  245.             return obj;
  246.           }
  247.           }
  248.       }
  249.     /* Can't grow in place.  Allocate a new object and copy. */
  250.     nobj = (byte *)alloc(new_num, elt_size, client_name);
  251.     if ( nobj == 0 )
  252.         return 0;
  253.     memcpy(nobj, obj, old_size);
  254.     alloc_free((char *)obj, old_num, elt_size, client_name);
  255.     alloc_print('>', '&', obj, new_size);
  256.     return nobj;
  257. }
  258.  
  259. /* Shrink an object. */
  260. /****** Note: the object must have been allocated at
  261.  ****** the current save level. */
  262. byte *
  263. alloc_shrink(byte *obj, uint old_num, uint new_num, uint elt_size,
  264.   const char *client_name)
  265. {    register alloc_state_ptr ap = &as_current;
  266.     uint old_size = old_num * elt_size;
  267.     uint new_size = new_num * elt_size;
  268.     if ( new_size == old_size ) return obj;
  269.     if ( old_size >= ap->big_size )
  270.       { /* Allocate a new block. */
  271.         byte *nobj = (byte *)alloc(new_num, elt_size, client_name);
  272.         if ( nobj == 0 ) return obj; /* can't shrink, leave as is */
  273.         memcpy(nobj, obj, new_size);
  274.         alloc_free((char *)obj, old_num, elt_size, client_name);
  275.         alloc_print('<', '&', obj, new_size);
  276.         return nobj;
  277.       }
  278.     else if ( obj == ap->ctop )
  279.       { /* Move the object up in place. */
  280.         /* memcpy doesn't do this properly. */
  281.         register byte *from = obj + new_size;
  282.         register byte *to = obj + old_size;
  283.         while ( from > obj ) *--to = *--from;
  284.         obj = ap->ctop = to;
  285.       }
  286.     else
  287.       { uint new_block_size = align_round(new_size);
  288.         alloc_free((char *)(obj + new_block_size),
  289.                1, align_round(old_size) - new_block_size,
  290.                "alloc_shrink");
  291.       }
  292.     alloc_print('<', ' ', obj, new_size);
  293.     return obj;
  294. }
  295.  
  296. /* ------ Private routines ------ */
  297.  
  298. /* Allocate (with malloc) an object too large to be put in a chunk. */
  299. /* Return NULL on failure. */
  300. private char *
  301. alloc_large(alloc_state_ptr ap, uint size, const char *client_name)
  302. {    alloc_block *mblock = (alloc_block *)
  303.         (*ap->palloc)(1, alloc_block_size + size, client_name);
  304.     char *block;
  305.     if ( mblock == NULL ) return NULL;
  306.     block = (char *)mblock + alloc_block_size;
  307.        alloc_print_large('+', '*', block, size);
  308.     mblock->next = ap->malloc_chain;
  309.     mblock->size = size;
  310.     mblock->save_level = ap->save_level;
  311.     mblock->cap = ap;
  312.     ap->malloc_chain = mblock;
  313.     ap->used += size;
  314.     ap->total += size;
  315.     return block;
  316. }
  317.  
  318. /* Allocate a new chunk.  Return true if successful. */
  319. #define chunk_head_size align_round(sizeof(alloc_chunk))
  320. private int
  321. alloc_add_chunk(register alloc_state_ptr ap, uint csize)
  322. {    char *space =
  323.         (*ap->palloc)(1, chunk_head_size + csize, "alloc chunk");
  324.     long discard;
  325.     if ( space == NULL )
  326.         return 0;
  327.     ap->num_chunks++;
  328.     /* Accumulate statistics */
  329.     ap->total += csize;
  330.     alloc_status(&ap->used, &discard);
  331.     /* Stash the state of the old chunk */
  332.     if ( ap->current_ptr != 0 )    /* check for very first chunk */
  333.         *ap->current_ptr = ap->current;
  334.     /* Initialize the new chunk */
  335.     ap->cbase = ap->cbot = (byte *)space + chunk_head_size;
  336.     ap->climit = ap->ctop = ap->cbot + csize;
  337.     ap->current.next = ap->current_ptr;
  338.     ap->current.save_level = ap->save_level;
  339.     ap->current_ptr = (alloc_chunk *)space;
  340.     return 1;
  341. }
  342. #undef chunk_head_size
  343.  
  344. /* Free a large object (allocated with malloc). */
  345. private void
  346. alloc_free_large(char *cobj, uint size, const char *client_name)
  347. {    alloc_block **prev;
  348.     alloc_block *mblock = (alloc_block *)(cobj - alloc_block_size);
  349.     alloc_state_ptr ap = mblock->cap;
  350.     if ( mblock->save_level == ap->save_level )
  351.      for ( prev = &ap->malloc_chain; *prev != 0; prev = &mblock->next )
  352.        {    mblock = *prev;
  353.         if ( (char *)mblock + alloc_block_size == cobj )
  354.            {    *prev = mblock->next;
  355.             ap->used -= size;
  356.             ap->total -= size;
  357.             (*ap->pfree)((char *)mblock,
  358.                     1, size + alloc_block_size,
  359.                     "large object");
  360.             alloc_print_large('-', '*', cobj, size);
  361.             return;
  362.            }
  363.        }
  364.     alloc_print('-', '?', cobj, size);
  365. }
  366.